package com.google.code.mergeinformer.controller;

import com.google.code.mergeinformer.constraint.Group;
import com.google.code.mergeinformer.model.AuthType;
import com.google.code.mergeinformer.model.Branch;
import com.google.code.mergeinformer.model.Project;
import com.google.code.mergeinformer.model.VcsAttributes;
import com.google.code.mergeinformer.util.Measured;
import com.google.code.mergeinformer.util.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.Collections;
import java.util.List;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

/**
 * @author Dmitry Golubev
 */
@Controller
public class ProjectController extends ControllerBase {

    private static final String MA_AUTH_TYPES = "authTypes";
    
    public static final String EDIT_PROJECT_VIEW = "editProject";

    @RequestMapping("/{projectShortName:.+}")
    public String showProject(@PathVariable String projectShortName) {
        final Project project = projectService.getByShortName(projectShortName);
        if (project == null) {
            return getFallback();
        }
        setCurrent(project, null);

        return HomeController.HOME_VIEW;
    }

    @RequestMapping(value = "/", params = "new-project")
    public String addProject(Model model) {
        putAuthTypes(model);
        preserveCurrent();
        return EDIT_PROJECT_VIEW;
    }

    @RequestMapping(value = "/", params = "create", method = POST, produces = APPLICATION_JSON_VALUE)
    public @ResponseBody ValidationResult createProject(@RequestBody Project project) {
        final ValidationResult result = validate(project);
        if (result.isValid()) {
            projectService.persist(project);
            result.setData(project.getShortName());

            pushNotifications(new Notification(
                    Notification.Type.success,
                    "New project",
                    "Project <b>" + project.getName() + "</b> was successfully created"
            ));
        }
        return result;
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "edit")
    public String editProject(@PathVariable String projectShortName, Model model) {
        final Project project = projectService.getByShortName(projectShortName);
        if (project == null) {
            return getFallback();
        }
        setCurrent(project, null);
        putAuthTypes(model);
        model.addAttribute("edit", Boolean.TRUE);
        
        return EDIT_PROJECT_VIEW;
    }
    
    @RequestMapping(value = "/{projectShortName:.+}", params = "apply", method = POST)
    public @ResponseBody ValidationResult saveChanges(@PathVariable String projectShortName, @RequestBody Project editedProject) {
        final Project project = projectService.getByShortName(projectShortName);
        if (project == null) {
            throw new IllegalArgumentException("Project shortName=" + projectShortName + " was not found");
        }
        final ValidationResult result = validate(editedProject);
        if (result.isValid()) {
            project.copyAttributes(editedProject);
            projectService.persist(project);
            result.setData(project.getShortName());

            pushNotifications(new Notification(
                    Notification.Type.success,
                    "Edit project",
                    "Project <b>" + project.getName() + "</b> was successfully edited"
            ));
        }
        return result;
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "delete")
    public String deleteProject(@PathVariable String projectShortName) {
        final Project project = projectService.getByShortName(projectShortName);
        final int tasks = project.getTasks().size();
        projectService.remove(project);

        pushNotifications(new Notification(
                Notification.Type.success,
                "Delete project",
                "Project <b>" + project.getName() + "</b> and it's <b>" + tasks + " tasks</b> were successfully deleted"
        ));

        if (isCurrentProject(projectShortName)) {
            return getRedirect();
        }

        return getFallback();
    }

    @RequestMapping(value = "/", params = "is-name-available", produces = APPLICATION_JSON_VALUE)
    public @ResponseBody Boolean isProjectNameAvailable(@RequestParam String name) {
        return !projectService.isNameExists(StringUtils.normalize(name));
    }

    @RequestMapping(value = "/", params = "is-short-name-available", produces = APPLICATION_JSON_VALUE)
    public @ResponseBody Boolean isProjectShortNameAvailable(@RequestParam String shortName) {
        return !projectService.isShortNameExists(StringUtils.normalize(shortName));
    }

    @RequestMapping(value = "/", params = {"test-url"}, method = POST, produces = APPLICATION_JSON_VALUE)
    public @ResponseBody ValidationResult testUrl(@RequestBody VcsAttributes vcsAttributes) {
        return validate(vcsAttributes, Group.Live.class);
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "execute")
    public String executeAllTasks(@PathVariable String projectShortName) {
        final Project project = projectService.getByShortName(projectShortName);
        if (project == null) {
            return getFallback();
        }

        final long elapsed = new Measured() {
            @Override
            protected void measuredBlock() {
                projectService.execute(project);
            }
        }.execute();

        final int tasksFailed = project.getFailedTasksCount();
        final String title = "Update project";
        final Notification notification;
        if (tasksFailed == 0) {
            notification = buildNotification(
                    Notification.Type.success,
                    title,
                    "All tasks were successfully updated",
                    elapsed
            );
        } else if (tasksFailed < project.getTasks().size()) {
            notification = buildNotification(
                    Notification.Type.notice,
                    title,
                    "<b>" + tasksFailed + "</b> of <b>" + project.getTasks().size() + "</b> tasks were failed",
                    elapsed
            );
        } else {
            notification = buildNotification(
                    Notification.Type.error,
                    title,
                    "All <b>" + tasksFailed + "</b> tasks were failed",
                    elapsed
            );
        }

        pushNotifications(notification);

        return getRedirect(project);
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "refresh-branches", produces = APPLICATION_JSON_VALUE)
    public @ResponseBody List<Branch> refreshBranches(@PathVariable String projectShortName) {
        final Project project = projectService.getByShortName(projectShortName);
        projectService.refreshBranches(project);
        Collections.sort(project.getBranches());
        return project.getBranches();
    }

    private void putAuthTypes(Model model) {
        model.addAttribute(MA_AUTH_TYPES, AuthType.values());
    }
}
